package com.jintian.smart.kernel.orm.query.impl;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import cn.hutool.core.util.ArrayUtil;

import com.jintian.smart.kernel.common.util.StringUtil;
import com.jintian.smart.kernel.orm.dto.Condition;
import com.jintian.smart.kernel.orm.query.ConditionOperatorEnum;
import com.jintian.smart.kernel.orm.query.IQueryBuilder;
import com.jintian.smart.kernel.orm.query.Query;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Function;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import org.elasticsearch.index.query.*;

public class ElastisSearchQueryBuilder implements IQueryBuilder<QueryBuilder> {

    @Override
    public QueryBuilder build(Query query) throws JSQLParserException {

        QueryBuilder combineCondition = buildCombineCondition(query.getConditionFormat(), query.getConditions());
        // 3.短语条件
        if (StringUtil.isNotEmpty(query.getPhrase())) {
            String[] targetFields = {};
            if (ArrayUtil.isNotEmpty(query.getHighLights())) {
                /**
                 * 默认情况下只有query的字段才被高亮，除非修改require_field_match为false
                 */
                Set<String> searchFields = new HashSet<>(Arrays.asList(query.getHighLights()));
                if (ArrayUtil.isNotEmpty(query.getColumns())) {
                    searchFields.addAll(Arrays.asList(query.getColumns()));
                }
                if (ArrayUtil.isNotEmpty(query.getConditions())) {
                    // 短语检索 排除条件字段
                    for (Condition c : query.getConditions()) {
                        searchFields.remove(c.getField());
                    }
                }
                targetFields = searchFields.toArray(new String[0]);
            }
            QueryBuilder phraseQuer = buildPhraseQuery(query.getPhrase(), targetFields, query.isIncludeAllTerms());
            if (combineCondition instanceof BoolQueryBuilder) {
                ((BoolQueryBuilder) combineCondition).must(phraseQuer);
            } else {
                combineCondition = QueryBuilders.boolQuery().must(combineCondition).must(phraseQuer);
            }
        }
        return combineCondition;
    }

    private static QueryBuilder buildPhraseQuery(String phrase, String[] targetFields, boolean and) {
        // boolQuery搜索方式
        // 先定义一个MultiMatchQuery
        MultiMatchQueryBuilder multiMatchQueryBuilder = null;
        if (targetFields == null || targetFields.length == 0) {
            multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(phrase);
        } else {
            multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(phrase, targetFields);
        }
        if (and) {
            // 默认多个单词是OR的关系
            multiMatchQueryBuilder.operator(Operator.AND);
        }
        return multiMatchQueryBuilder;
    }

    private static QueryBuilder buildCombineCondition(String conditionFormat, Condition[] conditions)
            throws JSQLParserException {
        BoolQueryBuilder ret = QueryBuilders.boolQuery();
        if (conditions == null || conditions.length == 0)
            return ret;

        if (StringUtil.isNotEmpty(conditionFormat)) {
            String sql = "select * from dual where " + fomatCondtion(conditionFormat);
            Select select = (Select) CCJSqlParserUtil.parse(sql);
            PlainSelect body = (PlainSelect) select.getSelectBody();
            Expression expression = body.getWhere();
            return binaryExpressionToQueryBuilder(expression, conditions);
        } else {
            // 遍历组合查询条件，将相关查询组合起来
            for (Condition condition : conditions) {
                if (StringUtil.isEmpty(condition.getOperator()))
                    continue;
                ret = ret.must(conditionToQueryBuilder(condition));
            }
        }
        return ret;
    }

    private static final Pattern CONDITION_SYMBOL_REGX = Pattern.compile("(%\\d+)");

    private static String fomatCondtion(String conditionFormat) {
        Matcher m = CONDITION_SYMBOL_REGX.matcher(conditionFormat);
        StringBuffer sb = new StringBuffer();
        int start = 0;
        while (m.find()) {
            sb.append(conditionFormat.substring(start, m.start()));
            sb.append("c(").append(m.group().substring(1)).append(")");
            start = m.end();
        }
        sb.append(conditionFormat.substring(start));
        return sb.toString();
    }

    private static QueryBuilder binaryExpressionToQueryBuilder(Expression expression, Condition[] conditions)
            throws JSQLParserException {
        if (expression instanceof AndExpression) {
            BoolQueryBuilder ret = QueryBuilders.boolQuery();
            AndExpression and = (AndExpression) expression;
            ret.must(binaryExpressionToQueryBuilder(and.getLeftExpression(), conditions))
                    .must(binaryExpressionToQueryBuilder(and.getRightExpression(), conditions));
            return ret;
        } else if (expression instanceof OrExpression) {
            BoolQueryBuilder ret = QueryBuilders.boolQuery();
            OrExpression or = (OrExpression) expression;
            ret.should(binaryExpressionToQueryBuilder(or.getLeftExpression(), conditions))
                    .should(binaryExpressionToQueryBuilder(or.getRightExpression(), conditions));
            return ret;
        } else if (expression instanceof Function) {
            String v = expression.toString();
            Condition condtion = conditions[Integer.parseInt(v.substring(2, v.length() - 1))];
            return conditionToQueryBuilder(condtion);
        } else if (expression instanceof Parenthesis) {
            return binaryExpressionToQueryBuilder(((Parenthesis) expression).getExpression(), conditions);
        }
        throw new JSQLParserException("不支持的SQL表达式:" + expression);
    }

    private static QueryBuilder conditionToQueryBuilder(Condition condition) {
        ConditionOperatorEnum operator = null;
        try {
            operator = ConditionOperatorEnum.valueOf(condition.getOperator());
        } catch (Exception ex) {
            throw new RuntimeException("错误操作符:" + condition.getOperator());
        }
        return operator.esFilter(condition);
    }
}
